# ----------------------------------------------------------------------------
#	(C) COPYRIGHT HiPi, 2014
#
#	FILE:
#		denon_avr_controller.py
#
#	DESCRIPTION:
#       This file contains the denon_avr_controller class
#
#   $Author: VNG $
#
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Imported Modules
# ----------------------------------------------------------------------------
from Pyside import QtCore
from denon_avr_hw import DenonAVRHW

# ----------------------------------------------------------------------------
# CLASS:
#   DenonAvrController
#
# DESCRIPTION:
#   TODO
#
# ----------------------------------------------------------------------------
class DenonAvrController(QtCore.QObject):

    # ----------------------------------------------------------
    # signals

    _signal_state_changed       = QtCore.Signal(str)
    _signal_connection_changed  = QtCore.Signal()
    _signal_error               = QtCore.Signal()


    # ----------------------------------------------------------
    # timers

    _keep_alive_timer        = QtCore.QTimer()

    # ----------------------------------------------------------
    # states

    STATE_INITIAL   = 0
    STATE_STANDBY   = 1
    STATE_PLAY      = 2

    # ----------------------------------------------------------
    # state mappings

    self._state_to_string = {
    STATE_INITIAL:      'INITIAL',
    STATE_STANDBY:      'STANDBY',
    STATE_PLAY:         'PLAYING',
    }

    # ----------------------------------------------------------
    def __init__(self):
        super(DenonAvrController, self).__init__()

        # self initialisation
        self._state = self.STATE_INITIAL
        self._connected = False

        # create an instance of the denon avr hw 
        self._hw = DevonAVRHW()
        self._hw.initialise()

        # start the keep alive timer
        self._keep_alive_timer.timeout.connect(self._keep_alive_expired)
        self._keep_alive_timer.start(750)

    # ----------------------------------------------------------
    # ----------------------------------------------------------
    @property
    def signal_state_changed(self):
        return self._signal_state_changed(str)

    # ----------------------------------------------------------
    @property
    def signal_error(self):
        return self._signal_error

    # ----------------------------------------------------------
    @property
    def signal_connection_changed(self):
        return self._signal_connected

    # ----------------------------------------------------------
    @property
    def state(self):
        return self._state

    @property
    def state_as_string(self):
        return self. _state_to_string[self._state]

    # ----------------------------------------------------------
    @property
    def settings(self):
        return self._settings

    @settings.setter
    def settings(self, value):
        self._settings = value

    # ----------------------------------------------------------
    def initialise(self):
        pass

    # ----------------------------------------------------------
    # ----------------------------------------------------------
    def shutdown(self):
        pass

    # ----------------------------------------------------------
    # ----------------------------------------------------------
    @QtCore.Slot()
    def _keep_alive_expired(self):

        rsp = self._hw.keep_alive()

        if (self._connected == False):
            if rsp != None:
                print 'connnected'
                self._connected = True
                self._signal_connected.emit(1)
                self._action(self.ACTION_READY)

        else:
            if rsp == None:
                print 'unconnected'
                self._connected = False
                self._signal_connected.emit(0)
                self._action(self.ACTION_DISCONNECTED)

    # ----------------------------------------------------------
    # ----------------------------------------------------------
    def _change_state(self, new_state):

        # save the current state
        old_state = self._state

        # check if this is a state change
        if new_state != old_state:
            self._state = new_state
            self._signal_state_changed.emit()

    # ----------------------------------------------------------
    def _find_transition(self, state, action):

        # get the transition table for the current state
        transitions = self._state_table[state]

        # search for a transition for this action
        if action in transitions:
            return transitions[action]

        return (None, None)

    # ----------------------------------------------------------
    def _action(self, action):

        # save the old state
        old_state = self._state

        # get the transition for this action
        (new_state, action_func) = self._find_transition(self._state, action)

        # call the action function
        if action_func != None:
            returned_state = action_func(self)
            if returned_state != None:
                new_state = returned_state

        if (new_state != None) and (new_state != old_state):

            # call the exit state function
            (_, exit_func) = self._find_transition(old_state, self.ACTION_STATE_EXIT)
            if exit_func != None:
                exit_func(self)

            # change the state
            self._change_state(new_state)

            # call the entry state function
            (_, entry_func) = self._find_transition(new_state, self.ACTION_STATE_ENTER)
            if entry_func != None:
                entry_func(self)

    # ----------------------------------------------------------    
    # ----------------------------------------------------------    
    def action_ready(self):
        pass

    # ----------------------------------------------------------
    # ----------------------------------------------------------
    def enter_state_standby(self):
        print "enter standby state"
        pass

    # ----------------------------------------------------------
    def exit_state_standby(self):
        print "exit standby state"
        pass

    # ----------------------------------------------------------
    # ----------------------------------------------------------
    def enter_state_play(self):
        print "enter play state"
        
    # ----------------------------------------------------------
    def exit_state_play(self):
        print "exit play state"

    # ----------------------------------------------------------
    def action_play_stop(self):
        pass

    # ----------------------------------------------------------
    # ----------------------------------------------------------
    def action_disconnected(self):
    
        # the serial port has been disconnected from the 
        # controller.
        return self.STATE_INITIAL

    # ----------------------------------------------------------
    # state machine

    _transition_table_initial = {
        ACTION_READY:       (STATE_STANDBY,             action_ready),
    }

    _transition_table_standby = {
        ACTION_STATE_ENTER: (None,                      enter_state_standby),
        ACTION_STATE_EXIT:  (None,                      exit_state_standby),
        ACTION_PLAY:        (STATE_PLAY,                None),        
        ACTION_DISCONNECTED:(None,                      action_disconnected),        
    }

    _transition_table_play   = {
        ACTION_STATE_ENTER: (None,                      enter_state_play),
        ACTION_STATE_EXIT:  (None,                      exit_state_play),
        ACTION_STOP_PLAY:   (STATE_STANDBY,             action_play_stop),
        ACTION_DISCONNECTED:(None,                      action_disconnected),
    }

    _state_table = {
        STATE_INITIAL:          _transition_table_initial,
        STATE_STANDBY:          _transition_table_standby,
        STATE_PLAY:             _transition_table_play,
    }

# ------------------------------------------------------------------------------------
# End Of File
# ------------------------------------------------------------------------------------
